#include "MyApp.h"
#include <fstream>
#include <stdlib.h>


#define _TESS_INDEX_BUFFER


TessObject	tessObjects[4];

float	glb_insideTess;
float	glb_edgeTess;
float	glb_SingleTessFactor;
bool	glb_bSingleTessFactor;
float	glb_sphereRadius;
float	glb_PSPDelta;
float	glb_PSPScale;

bool	glb_bAdditive;
bool	glb_bSphereMesh = false;

bool	glb_bOn;
float	glb_Range;

float	glb_sphereX;
float	glb_sphereY;
float	glb_sphereZ;

float	glb_lightDir[3] = { 0.0f, -1.0f, 0.0f };
float	glb_lightColor[] = {255, 255, 255, 1};


float	glb_fThornLength;



float const MyApp::GRID_WIDTH = 200.0f;
int const MyApp::N_SPHERES = 8;

// Tessellation GUI parameters
TessType			glb_selectedTessObject;
TessPartition		glb_selectedTessPartitioning;
bool				glb_tessWireframe;
float				glb_tessInsideFactor;
float				glb_tessEdgeFactor;
float				glb_terrainHeight			=	0.5f;
int					glb_terrainPatchDim			=	7;
float				glb_terrainMinTessFactor	=	40.0f;
float				glb_terrainMaxTessFactor	=	40.0f;
float				glb_terrainMinViewDist		=	20;
float				glb_terrainMaxViewDist		=	100;
bool				glb_bTerrainDebugView		=	false;
int					glb_iImplicitDepth			=	1;
bool				glb_bWireframe				=	false;


MyApp::MyApp() :m_implicitQuadDimension(128), _inputLayout(0), _vertexBuffer(0), _indexBuffer(0), 
				m_vbSingle(0), m_vbControlPoints(0), m_ibControlPoints(0), m_fxAlienVS(0),	m_fxAlienGS(0), m_fxExplosion(0), m_fxImplicit(0), m_fxTessSphere(0),
				m_fxTessSingle(0), m_fxTessBezierSurface(0), m_fxTessPSPSurface(0), m_fxTessMesh(0),
				m_fxTessTerrain(0), m_fxQuads(0), m_fxDynamicTerrain(0), m_texImplicit(0), 
				m_srvImplicit(0), m_rtvImplicit(0), m_srvRandom(0), m_srvTerrainHmap(0), 
				m_srvTerrainNmap(0), m_srvSpaceship(0), m_texDynamicHmap(0), m_rtvDynamicHmap(0),
				m_srvDynamicNmap(0), _fX(0)
{
	XMMATRIX I = XMMatrixIdentity();
	_worldMx = I;
	_viewMx = I;
	_projMx = I;
	
	glb_bOn = false;
	glb_insideTess = 10.0f;
	glb_edgeTess = 10.0f;
	glb_sphereRadius = 1.0f;
	glb_PSPDelta = 0.15f;
	glb_PSPScale = 0.25f;

	glb_Range = 0.1f;

	m_LButtonDown = false;
	m_bPaused = false;

	animationStarted = false;

	glb_sphereX = glb_sphereY = glb_sphereZ = 0;

	_inputLayout = NULL;
	_vertexBuffer = NULL;
	_indexBuffer = NULL;
	_fX = NULL;


}

void MyApp::safeDelete(IUnknown* res)
{
	if (res) {
		res->Release();
		res = 0;
	}
}

MyApp::~MyApp()
{
	
	safeDelete(_inputLayout);
	safeDelete(_vertexBuffer);
	safeDelete(_indexBuffer);
	safeDelete(m_vbSingle);
	safeDelete(m_vbControlPoints);
	safeDelete(m_ibControlPoints);
	
	safeDelete(m_fxAlienVS);
	safeDelete(m_fxAlienGS);
	safeDelete(m_fxExplosion);
	safeDelete(m_fxImplicit);
	safeDelete(m_fxTessSphere);
	safeDelete(m_fxTessSingle);
	safeDelete(m_fxTessBezierSurface);
	safeDelete(m_fxTessPSPSurface);
	safeDelete(m_fxTessMesh);
	safeDelete(m_fxTessTerrain);
	safeDelete(m_fxQuads);
	safeDelete(m_fxDynamicTerrain);

	safeDelete(m_texImplicit);
	safeDelete(m_srvImplicit);
	safeDelete(m_rtvImplicit);
	safeDelete(m_srvRandom);
	safeDelete(m_srvTerrainHmap);
	safeDelete(m_srvTerrainNmap);
	safeDelete(m_srvSpaceship);
	safeDelete(m_texDynamicHmap);
	safeDelete(m_rtvDynamicHmap);
	safeDelete(m_srvDynamicNmap);

	safeDelete(_fX);
	safeDelete(rs_Wireframe);
	

	TwTerminate();
}

void TW_CALL callback_Play(void* data)
{
	MyApp* app = (MyApp*)data;;
	
	for (unsigned int i=0; i<app->m_spheres.size(); i++)
		app->m_spheres[i].start();

	app->animationStarted = true;
}

void TW_CALL callback_Explode(void*data)
{
	MyApp* app = (MyApp*)data;

	for (unsigned int i=0; i<app->m_aliens.size(); i++) {
		if (AlienTypes::GS_ALIEN == app->m_aliens[i].type) {
			app->m_aliens[i].type = AlienTypes::EXP_ALIEN;
			app->m_aliens[i].expl_time = 0.0f;
		}
	}

	if (app->myAlien.type == AlienTypes::GS_ALIEN)
		app->myAlien.type = AlienTypes::EXP_ALIEN;

	app->myAlien.type = AlienTypes::EXP_ALIEN;
	app->myAlien.expl_time = 0.0f;
}

bool MyApp::initialise(HINSTANCE hInstance, int cmdShow) 
{
	if (!DxApp::initialise(hInstance, cmdShow, 1024, 768))
		return false;

	// Build FXs
	if (FAILED(buildFX())) {
		MessageBox(hWnd(), L"Cannot create effects", L"Error", MB_OK|MB_ICONERROR);
		return false;
	}

	// Build Geometry
	if (FAILED(buildGeometry())) {
		MessageBox(hWnd(), L"Cannot create geometry", L"Error", MB_OK|MB_ICONERROR);
		return false;
	}

	// Textures
	HRESULT hr = D3DX11CreateShaderResourceViewFromFileA(_dxDev, (PATH_MEDIA+string("noise_texture.jpg")).c_str(), 0, 0, &m_srvRandom, 0);
	if (FAILED(hr)) {
		MessageBoxA(0, "Error loading random texture", 0, 0);
		return false;
	}


	// flags for heightmap texture
	D3DX11_IMAGE_LOAD_INFO loadInfo;
	loadInfo.Width = D3DX11_DEFAULT;
	loadInfo.Height = D3DX11_DEFAULT;
	loadInfo.Depth = D3DX11_DEFAULT;
	loadInfo.FirstMipLevel = D3DX11_DEFAULT;
	loadInfo.MipLevels = D3DX11_DEFAULT;
	loadInfo.Usage = (D3D11_USAGE)D3DX11_DEFAULT;
	loadInfo.BindFlags =  D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; //D3DX11_DEFAULT;
	loadInfo.CpuAccessFlags = loadInfo.MiscFlags = D3DX11_DEFAULT;
	loadInfo.Format = DXGI_FORMAT_R32G32B32A32_FLOAT           ;
	loadInfo.Filter = D3DX11_DEFAULT;
	loadInfo.MipFilter = D3DX11_DEFAULT;
	loadInfo.pSrcInfo = NULL;

	hr = D3DX11CreateShaderResourceViewFromFileA(_dxDev, (PATH_MEDIA+string("heightmap.jpg")).c_str(), &loadInfo, 0, &m_srvTerrainHmap, 0);
	if (FAILED(hr)) {
		MessageBoxA(0, "Error loading terrain heightmap", 0, 0);
		return false;
	}

	hr = D3DX11CreateShaderResourceViewFromFileA(_dxDev, (PATH_MEDIA+string("normalmap.bmp")).c_str(), 0, 0, &m_srvTerrainNmap, 0);
	if (FAILED(hr)) {
		MessageBoxA(0, "Error loading terrain normalmap", 0, 0);
		return false;
	}

	hr = D3DX11CreateShaderResourceViewFromFileA(_dxDev, (PATH_MEDIA+string("space_ship.jpg")).c_str(), 0, 0, &m_srvSpaceship, 0);
	if (FAILED(hr)) {
		MessageBoxA(0, "Error loading spaceship texture", 0, 0);
		return false;
	}
	

	// Dynamic terrain
	ID3D11Resource* resHmap = 0;
	m_srvTerrainHmap->GetResource(&resHmap);

	D3D11_RENDER_TARGET_VIEW_DESC rtd;
	rtd.Format = DXGI_FORMAT_R32G32B32A32_FLOAT           ;
	rtd.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
	rtd.Texture2D.MipSlice = 0;
	hr = _dxDev->CreateRenderTargetView(resHmap, &rtd, &m_rtvDynamicHmap);
	if (FAILED(hr)) {
		MessageBoxA(0, "Error creating hmap rtv", 0, 0);
		return false;
	}
	////////////////////////////////////

	//rs_Wireframe
	D3D11_RASTERIZER_DESC rsd;
	ZeroMemory(&rsd, sizeof(rsd));
	rsd.FillMode = D3D11_FILL_WIREFRAME;
	rsd.CullMode = D3D11_CULL_BACK;
	rsd.DepthClipEnable = true;
	hr = _dxDev->CreateRasterizerState(&rsd, &rs_Wireframe);
	if (FAILED(hr))
		MessageBoxA(0, "error creating rs Wireframe", 0, 0);


	// Camera
	m_camera.setProjection(0.7f, width()/(float)height(), 1.0f, 500.0f);
	m_camera.setPosVector(0.0f, 30.0f, 0.0f);
	m_camera.setRightVector(1.0f, 0.0f, 0.0f);
	m_camera.setUpVector(0.0f,   1.0f, 0.0f);
	m_camera.setLookVector(0.0f, 0.0f, 1.0f);
	

	// myAlien
	myAlien.setPos(0,5,0);
	myAlien.type = AlienTypes::GS_ALIEN;
	myAlien.mesh = alienMesh;

	// Textures/Views for Implicit surfaces
	if (!createImplicitResources())
		return false;

	// Tessellation - control point(s) buffer

		
	//____ check for DX11 support__ 
	if (dx11Support())
	{
		// Single control point vertex buffer
		D3D11_BUFFER_DESC bdsc;
		bdsc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
		bdsc.CPUAccessFlags = 0;
		bdsc.MiscFlags = 0;
		bdsc.StructureByteStride = 0;
		bdsc.Usage = D3D11_USAGE_IMMUTABLE;


#ifdef _TESS_INDEX_BUFFER
		// Version 2 : 6 vertices - W/ Index Buffer
		const int numVertices = 6;
		Vertex ctrl_point[numVertices];
		bdsc.ByteWidth = sizeof(Vertex)*numVertices;
		ZeroMemory(ctrl_point, sizeof(Vertex)*numVertices); 

		ctrl_point[0].position = XMFLOAT3( 0.0f,  1.0f,  0.0f); // top
		ctrl_point[1].position = XMFLOAT3(-1.0f,  0.0f,  1.0f);	//       1 ------> 2 (+,+)
		ctrl_point[2].position = XMFLOAT3( 1.0f,  0.0f,  1.0f); //      
		ctrl_point[3].position = XMFLOAT3(-1.0f,  0.0f, -1.0f); // 
		ctrl_point[4].position = XMFLOAT3( 1.0f,  0.0f, -1.0f); // (-,-) 3 ------> 4
		ctrl_point[5].position = XMFLOAT3( 0.0f, -1.0f,  0.0f); // bottom

		UINT indices[24];
		//			top-front
		indices[0] = 0; indices[1] = 4; indices[2] = 3;
		//			top-right
		indices[3] = 0; indices[4] = 2; indices[5] = 4;
		//			top-left
		indices[6] = 0; indices[7] = 3; indices[8] = 1;
		//			top-back
		indices[9] = 0; indices[10] = 1; indices[11] = 2;
		//			bottom-front
		indices[12] = 5; indices[13] = 3; indices[14] = 4;
		//			bottom-right
		indices[15] = 5; indices[16] = 4; indices[17] = 2;
		//			bottom-left
		indices[18] = 5; indices[19] = 1; indices[20] = 3;
		//			bottom-back
		indices[21] = 5; indices[22] = 2; indices[23] = 1;

		D3D11_BUFFER_DESC idsc;
		idsc.BindFlags = D3D11_BIND_INDEX_BUFFER;
		idsc.CPUAccessFlags = 0;
		idsc.MiscFlags = 0;
		idsc.StructureByteStride = 0;
		idsc.Usage = D3D11_USAGE_IMMUTABLE;
		idsc.ByteWidth = sizeof(UINT) * 24;
		
		D3D11_SUBRESOURCE_DATA ibdata;
		ibdata.pSysMem = indices;
		ibdata.SysMemPitch = ibdata.SysMemSlicePitch = 0;

		// create index buffer
		hr = _dxDev->CreateBuffer(&idsc, &ibdata, &m_ibControlPoints);
		

#else
		// Version 1 : 24 vertices - No Index Buffer
		const int numVertices = 3*8;  // triangle list - 8 triangles
		Vertex ctrl_point[numVertices];
		bdsc.ByteWidth = sizeof(Vertex) * numVertices; // three control points
		ZeroMemory(ctrl_point, sizeof(Vertex)*numVertices); // octahedron
		// CW
		// top - front
		ctrl_point[0].position = XMFLOAT3( 0.0f, 1.0f,  0.0f);
		ctrl_point[1].position = XMFLOAT3( 1.0f, 0.0f, -1.0f);
		ctrl_point[2].position = XMFLOAT3(-1.0f, 0.0f, -1.0f);
		// top - right
		ctrl_point[3].position = XMFLOAT3( 0.0f, 1.0f,  0.0f);
		ctrl_point[4].position = XMFLOAT3( 1.0f, 0.0f,  1.0f);
		ctrl_point[5].position = XMFLOAT3( 1.0f, 0.0f, -1.0f);
		// top - left
		ctrl_point[6].position = XMFLOAT3( 0.0f, 1.0f,  0.0f);
		ctrl_point[7].position = XMFLOAT3(-1.0f, 0.0f, -1.0f);
		ctrl_point[8].position = XMFLOAT3( -1.0f, 0.0f, 1.0f);
		// top - back
		ctrl_point[9].position = XMFLOAT3( 0.0f, 1.0f,  0.0f);
		ctrl_point[10].position = XMFLOAT3(-1.0f, 0.0f,  1.0f);
		ctrl_point[11].position = XMFLOAT3( 1.0f, 0.0f,  1.0f);
		
		// bottom - front
		ctrl_point[12].position = XMFLOAT3( 0.0f, -1.0f,  0.0f);
		ctrl_point[13].position = XMFLOAT3(-1.0f,  0.0f, -1.0f);
		ctrl_point[14].position = XMFLOAT3( 1.0f,  0.0f, -1.0f);
		// bottom - right
		ctrl_point[15].position = XMFLOAT3( 0.0f, -1.0f,  0.0f);
		ctrl_point[16].position = XMFLOAT3( 1.0f,  0.0f, -1.0f);
		ctrl_point[17].position = XMFLOAT3( 1.0f,  0.0f,  1.0f);
		// bottom - left
		ctrl_point[18].position = XMFLOAT3( 0.0f, -1.0f,  0.0f);
		ctrl_point[19].position = XMFLOAT3(-1.0f,  0.0f,  1.0f);
		ctrl_point[20].position = XMFLOAT3(-1.0f,  0.0f, -1.0f);
		// bottom - back
		ctrl_point[21].position = XMFLOAT3( 0.0f, -1.0f,  0.0f);
		ctrl_point[22].position = XMFLOAT3( 1.0f,  0.0f,  1.0f);
		ctrl_point[23].position = XMFLOAT3(-1.0f,  0.0f,  1.0f);
#endif
	
		D3D11_SUBRESOURCE_DATA bdata;
		bdata.pSysMem = ctrl_point;
		bdata.SysMemPitch = bdata.SysMemSlicePitch = 0;

		hr = _dxDev->CreateBuffer(&bdsc, &bdata, &m_vbControlPoints);
		if (FAILED(hr)) {
			MessageBoxA(0, "Error creating control points buffer", 0, 0);
			return false;
		}

		// Bezier surface effect
		if (!createEffect((PATH_SHADERS+string("tessBezierSurface.fxo")).c_str(), &m_fxTessBezierSurface))
			MessageBoxA(0, "Error creating bezSurface effect", 0, 0);




		
		// PSP surface effect
		if (!createEffect((PATH_SHADERS+string("tessPSPSurface.fxo")).c_str(), &m_fxTessPSPSurface))
			MessageBoxA(0, "Error creating pspSurface effect", 0, 0);

		// Sphere effect
		if (!createEffect((PATH_SHADERS+string("tessSphere.fxo")).c_str(), &m_fxTessSphere))
			MessageBoxA(0, "Error creating tessSphere effect", 0, 0);

		// Mesh effect
		if (!createEffect((PATH_SHADERS+string("tessMesh.fxo")).c_str(), &m_fxTessMesh))
			MessageBoxA(0, "Error creating tessMesh effect", 0, 0);

		// Terrain effect
		if (!createEffect((PATH_SHADERS+string("tessTerrain.fxo")).c_str(), &m_fxTessTerrain))
			MessageBoxA(0, "Error creating tessMesh effect", 0, 0);
		
		

		///____SINGLE_________

		ZeroMemory(&bdsc, sizeof(bdsc));
		bdsc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
		bdsc.ByteWidth = sizeof(Vertex);
		bdsc.Usage = D3D11_USAGE_IMMUTABLE;
		Vertex vSingle[1];
		ZeroMemory(&vSingle, sizeof(vSingle));
		ZeroMemory(&bdata, sizeof(bdata));
		bdata.pSysMem = vSingle;
		hr = _dxDev->CreateBuffer(&bdsc, &bdata, &m_vbSingle);
		if (FAILED(hr)) MessageBoxA(0, "Error creating single VB", 0, 0);

		if (!createEffect((PATH_SHADERS+string("tessSingle.fxo")).c_str(), &m_fxTessSingle))
			MessageBoxA(0, "Error creating tessSingle effect", 0, 0);
		//_____________________
		
	}

	

	mySphere.tessFactor = 5;
	mySphere.radius = 10.0f;

	setupScene();

	// GUI
	TwInit(TW_DIRECT3D11, _dxDev);

	TwWindowSize(width(), height());
	TwBar* myBar;
	myBar = TwNewBar("myBar");
	

	TwAddButton(myBar, "Play", callback_Play, (void*)this, 0);
	TwAddButton(myBar, "Explode", callback_Explode, (void*)this, 0);


	



	// __________GS Alien
	TwAddVarRW(myBar, "Thorn length", TW_TYPE_FLOAT, &glb_fThornLength, "group=GS_Alien min=0.0 max=1.0 step=0.05");


	// __________Selected Tessellated Object
	
	// Partitioning
	TwEnumVal  twTessPartitioning[] = { { TessPartitioning::FRAC_EVEN, "Frac. Even" }, { TessPartitioning::FRAC_ODD, "Frac. Odd" }, 
										{ TessPartitioning::INTEGER, "Integer" }, { TessPartitioning::POW2 , "Pow2" } };

	TwType twTessPartition;
	twTessPartition = TwDefineEnum("TessPartitioning", twTessPartitioning, 4);
	TwAddVarRW(myBar, "Partitioning Method", twTessPartition, &glb_selectedTessPartitioning, "group=Tessellated_Objects");
	
	// Wireframe
	TwAddVarRW(myBar, "Wireframe", TW_TYPE_BOOL8, &glb_tessWireframe, "group=Tessellated_Objects");
	// Inside Factor
	TwAddVarRW(myBar, "Inside Factor", TW_TYPE_FLOAT, &glb_insideTess, "group=Tessellated_Objects min=1 max=64 step=1");
	// Edge Factor
	TwAddVarRW(myBar, "Edge Factor", TW_TYPE_FLOAT, &glb_edgeTess, "group=Tessellated_Objects min=1 max=64 step=1");
	// Common inside/edge factor
	TwAddVarRW(myBar, "Common Factor", TW_TYPE_FLOAT, &glb_SingleTessFactor, "group=Tessellated_Objects min=1 max=64 step=1");
	TwAddVarRW(myBar, "Single Factor", TW_TYPE_BOOL8, &glb_bSingleTessFactor, "");
	/////////////////////
	
	TwAddVarRW(myBar, "Terrain Height", TW_TYPE_FLOAT, &glb_terrainHeight, "group=Terrain min=0.0 max=1.0 step=0.05");
	TwAddVarRW(myBar, "Patches Dimension", TW_TYPE_INT32, &glb_terrainPatchDim, "group=Terrain min=1 max=10 step=1");

	TwAddVarRW(myBar, "Min Tess. Factor", TW_TYPE_FLOAT, &glb_terrainMinTessFactor, "group=Terrain min=1 max=63 step=1");
	TwAddVarRW(myBar, "Max Tess. Factor", TW_TYPE_FLOAT, &glb_terrainMaxTessFactor, "group=Terrain min=1 max=63 step=1");

	TwAddVarRW(myBar, "Min View Dist", TW_TYPE_FLOAT, &glb_terrainMinViewDist, "group=Terrain min=0.0 max=300.0 step=1.05");
	TwAddVarRW(myBar, "Max View Dist", TW_TYPE_FLOAT, &glb_terrainMaxViewDist, "group=Terrain min=0.0 max=300.0 step=1.05");

	TwAddVarRW(myBar, "Debug View", TW_TYPE_BOOL8, &glb_bTerrainDebugView, "group=Terrain");

	// Terrain Editing
	TwAddVarRW(myBar, "Power", TW_TYPE_BOOL8, &glb_bOn, "group=Terrain_Edit");
	TwAddVarRW(myBar, "Additive", TW_TYPE_BOOL8, &glb_bAdditive, "group=Terrain_Edit");
	TwAddVarRW(myBar, "Range", TW_TYPE_FLOAT, &glb_Range, "group=Terrain_Edit min=0.0 max=1.0 step=0.05");

	// Light
	TwAddVarRW(myBar, "Light Direction", TW_TYPE_DIR3F, &glb_lightDir, "");
	TwAddVarRW(myBar, "Light Color", TW_TYPE_COLOR4F, &glb_lightColor, "");

	// Implicit Depth
	TwAddVarRW(myBar, "Implicit Depth", TW_TYPE_INT32, &glb_iImplicitDepth, "group=Implicit min=1 max=3");

	TwAddVarRW(myBar, "Wireframe Mode", TW_TYPE_BOOL8, &glb_bWireframe, "");
	
	TwAddVarRW(myBar, "Sphere From Mesh", TW_TYPE_BOOL8, &glb_bSphereMesh, "");


	return true;
}


void MyApp::setMaterial(ID3DX11Effect* fx, const Material& material)
{
	ID3DX11EffectShaderResourceVariable* fx_tex_diffuse  = fx->GetVariableByName("tex_diffuse")->AsShaderResource();
	ID3DX11EffectShaderResourceVariable* fx_tex_specular = fx->GetVariableByName("tex_specular")->AsShaderResource();
	ID3DX11EffectShaderResourceVariable* fx_tex_normal   = fx->GetVariableByName("tex_normal")->AsShaderResource();
	ID3DX11EffectVariable*	fx_mat_params 				 = fx->GetVariableByName("mat_params");

	fx_mat_params->SetRawValue((void*)&material, 0, 64);

	if (material.pTextureRV10 != (ID3D11ShaderResourceView*)0)
		fx_tex_diffuse->SetResource(material.pTextureRV10);

	if (material.pTextureSpecularPRV10 != (ID3D11ShaderResourceView*)0)
		fx_tex_specular->SetResource(material.pTextureSpecularPRV10);

	if (material.pTextureNormalPRV10 != (ID3D11ShaderResourceView*)0)
		fx_tex_normal->SetResource(material.pTextureNormalPRV10);

}

bool MyApp::createEffect(const char* filename, ID3DX11Effect** fx)
{
	// Load and create the pre-compiled shader code
	std::vector<char> compiledShader(0);
	if (!loadEffects(filename, compiledShader)) {
		OutputDebugStringA("Error: effect file not loaded");
		return false;
	}

	HRESULT hr = D3DX11CreateEffectFromMemory(&compiledShader[0], compiledShader.size(), 0, _dxDev, fx);
	if (FAILED(hr))
		return false;

	return true;
	
}

bool MyApp::createImplicitResources()
{
	HRESULT hr0, hr1, hr2;
	int arraySize = 5;

	D3D11_TEXTURE2D_DESC tdsc;
	tdsc.ArraySize = arraySize;
	tdsc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
	tdsc.CPUAccessFlags = 0;
	tdsc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
	tdsc.Height = m_implicitQuadDimension;
	tdsc.Width = m_implicitQuadDimension;
	tdsc.MipLevels = 1;
	tdsc.MiscFlags = 0;
	tdsc.SampleDesc.Count = 1;
	tdsc.SampleDesc.Quality = 0;
	tdsc.Usage = D3D11_USAGE_DEFAULT;
	hr0 = _dxDev->CreateTexture2D(&tdsc, 0, &m_texImplicit);
	
	//______

	D3D11_SHADER_RESOURCE_VIEW_DESC sdsc;
	sdsc.Format = tdsc.Format;
	sdsc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
	sdsc.Texture2DArray.ArraySize = arraySize;
	sdsc.Texture2DArray.FirstArraySlice = 0;
	sdsc.Texture2DArray.MipLevels = 1;
	sdsc.Texture2DArray.MostDetailedMip = 0;
	hr1 = _dxDev->CreateShaderResourceView(m_texImplicit, &sdsc, &m_srvImplicit);

	//_______

	D3D11_RENDER_TARGET_VIEW_DESC rtdsc;
	rtdsc.Format = tdsc.Format;
	rtdsc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
	rtdsc.Texture2DArray.ArraySize = arraySize;
	rtdsc.Texture2DArray.FirstArraySlice = 0;
	rtdsc.Texture2DArray.MipSlice = 0;
	hr2 = _dxDev->CreateRenderTargetView(m_texImplicit, &rtdsc, &m_rtvImplicit);

	if (FAILED(hr0) || FAILED(hr1) || FAILED(hr2)) {
		MessageBoxA(0, "Error creating implicit resources", 0, 0);
		return false;
	}
	return true;
}


void MyApp::drawImplicitToTexture()
{

	// store current Input Layout & Primitive Topology
	ID3D11InputLayout* ia;
	D3D11_PRIMITIVE_TOPOLOGY primTopology;
	_dxImmedDC->IAGetInputLayout(&ia);
	_dxImmedDC->IAGetPrimitiveTopology(&primTopology);
	ID3D11RenderTargetView* rtv;
	ID3D11DepthStencilView* dsv;
	_dxImmedDC->OMGetRenderTargets(1, &rtv, &dsv);
	D3D11_VIEWPORT vp;
	unsigned int num_vp = 1;
	_dxImmedDC->RSGetViewports(&num_vp, &vp);

	// no vertex buffer needs a null input layout
	_dxImmedDC->IASetInputLayout(0);
	_dxImmedDC->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
	
	// Set Texture Array as Render Target
	float clearColor[4] = { 0.6f, 0.7f, 0.5f, 1.0f };
	_dxImmedDC->ClearRenderTargetView(m_rtvImplicit, clearColor);
	_dxImmedDC->OMSetRenderTargets(1, &m_rtvImplicit, 0);
	D3D11_VIEWPORT new_vp;
	new_vp.Height = new_vp.Width = (float)m_implicitQuadDimension;
	new_vp.MaxDepth = 1.0f;		new_vp.MinDepth = 0.0f;
	new_vp.TopLeftX = 0.0f;		new_vp.TopLeftY = 0.0f;
	_dxImmedDC->RSSetViewports(1, &new_vp);

	// Variables
	XMMATRIX mWorld = XMMatrixIdentity();
	setMatrixVar(m_fxImplicit, (void*)&mWorld, "World");
	setMatrixVar(m_fxImplicit, (void*)&m_camera.getViewMatrix(), "View");
	setMatrixVar(m_fxImplicit, (void*)&m_camera.getProjectionMatrix(), "Projection");
	setFloatVar(m_fxImplicit, 0, "Time");
	setVectorVar(m_fxImplicit, (void*)&m_camera.getPosVector(), "eyePos");
	setIntVar(m_fxImplicit, glb_iImplicitDepth, "iDepth");

	XMVECTOR det;
	XMMATRIX viewInverse = XMMatrixInverse(&det, m_camera.getViewMatrix());
	setMatrixVar(m_fxImplicit, (void*)&viewInverse, "mInverseView");

	// draw 4 vertices 
	ID3DX11EffectTechnique * tech = 0;
	tech = m_fxImplicit->GetTechniqueByIndex(0);

	for (int i=0; i<5; i++)
	{
		setIntVar(m_fxImplicit, i, "iArrayIndex");
		tech->GetPassByIndex(0)->Apply(0, _dxImmedDC);
		_dxImmedDC->Draw( 4, 0 );
	}

	// restore Input Layout & Primitive Topology
	_dxImmedDC->RSSetViewports(num_vp, &vp);
	_dxImmedDC->IASetInputLayout(ia);
	_dxImmedDC->IASetPrimitiveTopology(primTopology);
	_dxImmedDC->OMSetRenderTargets(1, &rtv, dsv);
}


void MyApp::drawTessSphereFromMesh()
{

	//

	if (!dx11Support())
		return;

	// set vertex buffer
	UINT stride = sizeof(Vertex);
	UINT offset = 0;
	_dxImmedDC->IASetVertexBuffers(0, 1, &m_alienModel.vb, &stride, &offset);
	_dxImmedDC->IASetIndexBuffer(m_alienModel.ib, DXGI_FORMAT_R32_UINT, 0);

	// set topology
	_dxImmedDC->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST);
	// set input layout
	_dxImmedDC->IASetInputLayout(_inputLayout);

	// effect params
	XMMATRIX mWVP = XMMatrixIdentity() * m_camera.getViewMatrix() * m_camera.getProjectionMatrix();
	setMatrixVar(m_fxTessSingle, (void*)&mWVP, "m_WorldViewProj");
	setFloatVar(m_fxTessSingle, glb_insideTess, "fInsideTess");
	setFloatVar(m_fxTessSingle, glb_edgeTess, "fEdgeTess");
	setFloatVar(m_fxTessSingle, glb_sphereRadius, "fRadius");

	// draw
	ID3DX11EffectTechnique* fxTech;
	fxTech = m_fxTessSingle->GetTechniqueByName("TessSphere");
	
	fxTech->GetPassByIndex(0)->Apply(0, _dxImmedDC);

	_dxImmedDC->DrawIndexed(m_alienModel.numIndices, DXGI_FORMAT_R32_UINT, 0);
}

void MyApp::drawTessSphereFromOct(Sphere* sphere)
{
	if (!dx11Support())
		return;

	bool tess = true;


	// set vertex buffer
	UINT stride = sizeof(Vertex);
	UINT offset = 0;
	_dxImmedDC->IASetVertexBuffers(0, 1, &m_vbControlPoints, &stride, &offset);

	// set topology
	if (tess)
		_dxImmedDC->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST);
	else
		_dxImmedDC->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	// set input layout
	_dxImmedDC->IASetInputLayout(_inputLayout);

	// effect params
	XMMATRIX mWVP = XMMatrixIdentity() * m_camera.getViewMatrix() * m_camera.getProjectionMatrix();
	setMatrixVar(m_fxTessSphere, (void*)&mWVP, "m_WorldViewProj");
	setFloatVar(m_fxTessSphere, glb_insideTess, "fInsideTess");
	setFloatVar(m_fxTessSphere, glb_edgeTess, "fEdgeTess");
	
	setFloatVar(m_fxTessSphere, sphere->radius, "fRadius");
	setVectorVar(m_fxTessSphere, (void*)&(sphere->getPos()), "vPos");
	setIntVar(m_fxTessSphere, sphere->tessFactor, "iTessFactor");
	setSrvVar(m_fxTessSphere, this->m_srvSpaceship, "texSpaceship");
	passLightParams(m_fxTessSphere);

	// draw
	ID3DX11EffectTechnique* fxTech;
	if (tess)
		fxTech = m_fxTessSphere->GetTechniqueByName("TessSphere");
	else
		fxTech = m_fxTessSphere->GetTechniqueByName("NoTess");

	fxTech->GetPassByIndex(0)->Apply(0, _dxImmedDC);


#ifdef _TESS_INDEX_BUFFER
	_dxImmedDC->IASetIndexBuffer(m_ibControlPoints, DXGI_FORMAT_R32_UINT, 0);
	_dxImmedDC->DrawIndexed(24, 0, 0);
#else
	_dxImmedDC->Draw(24, 0);
#endif

}

void MyApp::drawTessBezierSurface(XMFLOAT3 pos, XMFLOAT3 target)
{
	if (!dx11Support())
		return;

	// set vertex buffer
	UINT stride = sizeof(Vertex);
	UINT offset = 0;
	_dxImmedDC->IASetVertexBuffers(0, 1, &m_vbSingle, &stride, &offset);

	// set topology
	_dxImmedDC->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST);
	// set input layout
	_dxImmedDC->IASetInputLayout(_inputLayout);


	// effect params
	XMMATRIX mWVP = XMMatrixIdentity() * m_camera.getViewMatrix() * m_camera.getProjectionMatrix();
	setMatrixVar(m_fxTessBezierSurface, (void*)&mWVP, "m_WorldViewProj");
	setFloatVar(m_fxTessBezierSurface, glb_insideTess, "fInsideTess");
	setFloatVar(m_fxTessBezierSurface, glb_edgeTess, "fEdgeTess");
	setVectorVar(m_fxTessBezierSurface, (void*)&pos, "vPosition");
	setVectorVar(m_fxTessBezierSurface, (void*)&target, "vTargetPos");
	setSrvVar(m_fxTessBezierSurface, m_srvTerrainHmap, "texTerrainHeight");
	passLightParams(m_fxTessBezierSurface);

	// draw
	ID3DX11EffectTechnique* fxTech;
	if (glb_selectedTessPartitioning == TessPartitioning::INTEGER)
		fxTech = m_fxTessBezierSurface->GetTechniqueByName("TessBezierSurface_Integer");
	else if (glb_selectedTessPartitioning == TessPartitioning::FRAC_EVEN)
		fxTech = m_fxTessBezierSurface->GetTechniqueByName("TessBezierSurface_FracEven");
	else 
		fxTech = m_fxTessBezierSurface->GetTechniqueByName("TessBezierSurface_FracOdd");

	fxTech->GetPassByIndex(0)->Apply(0, _dxImmedDC);

	_dxImmedDC->Draw(1, 0);
}

HRESULT MyApp::buildFX() 
{
	if (!createEffect((PATH_SHADERS+string("vs_alien.fxo")).c_str(), &m_fxAlienVS))
		MessageBoxA(0, "Error creating AlienVS effect", 0, 0);

	if (!createEffect((PATH_SHADERS+string("gs_alien.fxo")).c_str(), &m_fxAlienGS))
		MessageBoxA(0, "Error creating AlienGS effect", 0, 0);
	
	if (!createEffect((PATH_SHADERS+string("explosion.fxo")).c_str(), &m_fxExplosion))
		MessageBoxA(0, "Error creating Explosion effect", 0, 0);
	
	if (!createEffect((PATH_SHADERS+string("implicit.fxo")).c_str(), &m_fxImplicit))
		MessageBoxA(0, "Error creating Implicit effect", 0, 0);

	if (!createEffect((PATH_SHADERS+string("quads.fxo")).c_str(), &m_fxQuads))
		MessageBoxA(0, "Error creating quads effect", 0, 0);

	if (!createEffect((PATH_SHADERS+string("dynamicTerrain.fxo")).c_str(), &m_fxDynamicTerrain))
		MessageBoxA(0, "Error creating dynamic terrain effect", 0, 0);


    // Vertex Input Layout
	D3D11_INPUT_ELEMENT_DESC layout[] = {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
        { "NORMAL", 0,  DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TANGENT", 0,  DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEX_COORDS", 0,  DXGI_FORMAT_R32G32_FLOAT, 0, 36, D3D11_INPUT_PER_VERTEX_DATA, 0 }
    };

	D3DX11_PASS_DESC passDesc;
	ID3DX11EffectTechnique* tech;
	tech = m_fxAlienVS->GetTechniqueByIndex(0);
	HRESULT hr = tech->GetPassByIndex(0)->GetDesc(&passDesc);

	hr = _dxDev->CreateInputLayout(layout, 4, passDesc.pIAInputSignature, passDesc.IAInputSignatureSize, &_inputLayout);
	if (FAILED(hr))
		MessageBoxA(0, "Error creating input layout", 0, 0);

	return S_OK;
}



void MyApp::onMouseMove(LPARAM lparam)
{
	unsigned int x = LOWORD(lparam);
	unsigned int y = HIWORD(lparam);

	m_mouseX = x;
	m_mouseY = y;
}


void MyApp::editTerrain()
{
	if (!glb_bOn || !m_LButtonDown)
		return;

	//------------------------------------------------------------
	int w = width(), h = height();

	XMMATRIX P = m_camera.getProjectionMatrix();

	// Compute picking ray in view space.
	float vx = (+2.0f*m_mouseX/w  - 1.0f)/P(0,0);
	float vy = (-2.0f*m_mouseY/h + 1.0f)/P(1,1);

	// Ray definition in view space.
	XMVECTOR rayOrigin = XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f);
	XMVECTOR rayDir    = XMVectorSet(vx, vy, 1.0f, 0.0f);

	// Tranform ray to local space of Mesh.
	XMMATRIX V = m_camera.getViewMatrix();
	XMMATRIX invView = XMMatrixInverse(&XMMatrixDeterminant(V), V);

	rayOrigin = XMVector3TransformCoord(rayOrigin, invView);
	rayDir = XMVector3TransformNormal(rayDir, invView);

	rayDir = XMVector3Normalize(rayDir);
	

	XMFLOAT4 oo, rr;
	XMStoreFloat4(&oo, rayOrigin);
	XMStoreFloat4(&rr, rayDir);


	XMVECTOR a = XMVectorSet(0,0,0,1); // point on plane
	XMVECTOR n = XMVectorSet(0,1,0,0); // plane's normal

	XMVECTOR res = XMVector3Dot((a - rayOrigin), n) / XMVector4Dot(rayDir, n);
	
	float t = XMVectorGetX(res);

	XMVECTOR hit_point = rayOrigin + t*rayDir;
	XMFLOAT4 hp;
	XMStoreFloat4(&hp, hit_point);


	XMFLOAT2 coords;
	coords.x = XMVectorGetX(hit_point) / GRID_WIDTH + 0.5f;
	coords.y = XMVectorGetZ(hit_point) / GRID_WIDTH + 0.5f;
	coords.y = 1.0f - coords.y;

	XMFLOAT4 mo, md;
	XMStoreFloat4(&mo, rayOrigin);
	XMStoreFloat4(&md, rayDir);

	//------------------------------------------------------------


	// save/set Render Target View
	ID3D11DepthStencilView* dsv;
	ID3D11RenderTargetView* rtv;
	_dxImmedDC->OMGetRenderTargets(1, &rtv, &dsv);
	D3D11_VIEWPORT vp;
	UINT vp_num = 1;
	_dxImmedDC->RSGetViewports(&vp_num, &vp);

	D3D11_VIEWPORT new_vp;
	new_vp.Height = 1024;
	new_vp.Width = 1024;
	new_vp.MaxDepth =1.0f;
	new_vp.MinDepth = 0.0f;
	new_vp.TopLeftX = new_vp.TopLeftY = 0.0f;
	_dxImmedDC->RSSetViewports(1, &new_vp);


	_dxImmedDC->OMSetRenderTargets(1, &m_rtvDynamicHmap, 0);
	
	// save input layout
	ID3D11InputLayout* ia;
	_dxImmedDC->IAGetInputLayout(&ia);

	// set null layout
	_dxImmedDC->IASetInputLayout(0);
	_dxImmedDC->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);

	setBoolVar(m_fxDynamicTerrain, glb_bAdditive, "bAdditive");
	setVectorVar(m_fxDynamicTerrain, (void*)&coords, "vCoords");
	setFloatVar(m_fxDynamicTerrain, glb_Range, "fRange");

	// draw 4 points (-> quad)
	ID3DX11EffectTechnique* tech;
	tech = m_fxDynamicTerrain->GetTechniqueByIndex(0);
	tech->GetPassByIndex(0)->Apply(0, _dxImmedDC);
	_dxImmedDC->Draw(4, 0);

	// restore IA layout, rtv, dsv
	_dxImmedDC->OMSetRenderTargets(1, &rtv, dsv);
	_dxImmedDC->IASetInputLayout(ia);
	_dxImmedDC->OMSetBlendState(0, 0, 0xffffffff);
	_dxImmedDC->OMSetDepthStencilState(0, 0);
	_dxImmedDC->RSSetViewports(1, &vp);


}

void MyApp::onLButtonDown()
{
	m_LButtonDown = true;
}

void MyApp::onLButtonUp()
{
	m_LButtonDown = false;
}


HRESULT MyApp::buildGeometry() 
{
	// inform obj loader about media folder
	m_objLoader.setFolderPath(PATH_MEDIA);
   
	//_____Alien mesh______________
						
	//alien meshes
	Material mat_alien;
	m_objLoader.load(_dxDev, "untitled.obj", &(m_alienModel.vb), &(m_alienModel.ib), mat_alien, 
					m_alienModel.numVertices, m_alienModel.numIndices);

	
	alienMesh.model = &m_alienModel;
	alienMesh.material = mat_alien;



    return S_OK;
}

void MyApp::updateScene() 
{
	static float alienCreation = 5.5f;
	static float aliensCreated = 0;
	static int currentAlienType = 0;

	float step_view = 0.01f;

	if(GetAsyncKeyState(0x26) & 0x8000) m_camera.rotateX(-step_view);	// up
	if(GetAsyncKeyState(0x28) & 0x8000) m_camera.rotateX(+step_view);	// down
	if(GetAsyncKeyState(0x25) & 0x8000) m_camera.rotateY(-step_view);		// left
	if(GetAsyncKeyState(0x27) & 0x8000) m_camera.rotateY(+step_view);		// right

	float scale = 20.0f;
	if (GetAsyncKeyState('W') & 0x8000) m_camera.walkForward(+step_view*scale);
	if (GetAsyncKeyState('S') & 0x8000) m_camera.walkForward(-step_view*scale);
	if (GetAsyncKeyState('A') & 0x8000) m_camera.walkSide(-step_view*scale);
	if (GetAsyncKeyState('D') & 0x8000) m_camera.walkSide(+step_view*scale);		


	mySphere.setPos(glb_sphereX, glb_sphereY, glb_sphereZ);

	
	if(!m_bPaused)
	{
		int spheresLanded = 0;

		// update spheres
		for (unsigned int i=0; i<m_spheres.size(); i++) 
		{
			m_spheres[i].updatePos(0.5f);
			
			if (animationStarted && m_spheres[i].isInMotion() == false) 
				spheresLanded++;
		}

		// update aliens
		for (unsigned int i=0; i<m_aliens.size(); i++)
		{
			m_aliens[i].updatePos(0.01f);

			if (!m_aliens[i].isInMotion()) {
				// compute new target pos
				if (m_aliens[i].type == AlienTypes::VS_ALIEN && m_aliens[i].type == AlienTypes::HS_ALIEN) {
					XMFLOAT3 targetPos;
					targetPos.x = rand() % (unsigned int)GRID_WIDTH - GRID_WIDTH / 2.0f;
					targetPos.y = m_aliens[i].getPos().y;
					targetPos.z = rand() % (unsigned int)GRID_WIDTH - GRID_WIDTH / 2.0f;
					m_aliens[i].setTargetPos(targetPos.x, targetPos.y, targetPos.z);
					m_aliens[i].start();
				}
				else if (m_aliens[i].type == AlienTypes::GS_ALIEN) {
					// if it's a GS_Alien just make it explode
					m_aliens[i].type = AlienTypes::EXP_ALIEN;
					m_aliens[i].expl_time = 0.0f;
				}

			}
		}

		// Create Alien
		if (spheresLanded == N_SPHERES && aliensCreated <= 12) 
		{
			alienCreation -= /*0.005f*/ 1.0f; // create all aliens immediately 

			if (alienCreation <= 0.0f) {
				// pick sphere 
				int s = rand() % N_SPHERES;
				// Alien
				Alien myAlien;
				XMFLOAT3 pos = m_spheres[s].getPos();
				pos.y -= 5.0f;
				myAlien.setPos(pos.x, pos.y, pos.z);
				// Alien type
				int alientype = (int)(currentAlienType / 2);
				//alientype = 1;
				if (alientype == 0)
					myAlien.type = AlienTypes::VS_ALIEN;
				else if (alientype == 1)
					myAlien.type = AlienTypes::GS_ALIEN;
				else if (alientype == 2)
					myAlien.type = AlienTypes::EXP_ALIEN;
				else if (alientype == 3)
					myAlien.type = AlienTypes::HS_ALIEN;
				else if (alientype == 4)
					myAlien.type = AlienTypes::BEZ_ALIEN;
				else if (alientype == 5)
					myAlien.type = AlienTypes::PSP_ALIEN;


				myAlien.mesh = alienMesh;
				// target pos
				XMFLOAT3 targetPos;
				targetPos.x = rand() % (unsigned int)GRID_WIDTH - GRID_WIDTH / 2.0f;
				targetPos.y = myAlien.getPos().y;
				targetPos.z = rand() % (unsigned int)GRID_WIDTH - GRID_WIDTH / 2.0f;
				myAlien.setTargetPos(targetPos.x, targetPos.y, targetPos.z);

				myAlien.start();
				m_aliens.push_back(myAlien);
				alienCreation = 5.0f;
				aliensCreated++;
				currentAlienType++;
			}
		}
	}

	// GUI 
	if (glb_bSingleTessFactor) {
		glb_insideTess = glb_edgeTess = glb_SingleTessFactor;
	}

	light.dir = XMFLOAT3(glb_lightDir[0], glb_lightDir[1], glb_lightDir[1]);
	XMVECTOR v = XMLoadFloat3(&light.dir);
	v = XMVector3Normalize(v);
	XMStoreFloat3(&light.dir, v);

	light.diffuse = XMFLOAT3(glb_lightColor[0]/256.0f, glb_lightColor[1]/256.0f, 
								glb_lightColor[2]/256.0f);

}

void MyApp::drawImplicitObjects()
{

	// store current Input Layout & Primitive Topology
	ID3D11InputLayout* ia;
	D3D11_PRIMITIVE_TOPOLOGY primTopology;
	_dxImmedDC->IAGetInputLayout(&ia);
	_dxImmedDC->IAGetPrimitiveTopology(&primTopology);

	// no vertex buffer needs a null input layout
	_dxImmedDC->IASetInputLayout(0);
	_dxImmedDC->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
	
	// Variables
	XMMATRIX mWorld = XMMatrixIdentity();
	setMatrixVar(m_fxImplicit, (void*)&mWorld, "World");
	setMatrixVar(m_fxImplicit, (void*)&m_camera.getViewMatrix(), "View");
	setMatrixVar(m_fxImplicit, (void*)&m_camera.getProjectionMatrix(), "Projection");
	setFloatVar(m_fxImplicit, 0, "Time");
	setVectorVar(m_fxImplicit, (void*)&m_camera.getPosVector(), "eyePos");

	XMVECTOR det;
	XMMATRIX viewInverse = XMMatrixInverse(&det, m_camera.getViewMatrix());
	setMatrixVar(m_fxImplicit, (void*)&viewInverse, "mInverseView");

	// draw 4 vertices 
	ID3DX11EffectTechnique * tech = 0;
	tech = m_fxImplicit->GetTechniqueByIndex(0);
	tech->GetPassByIndex(0)->Apply(0, _dxImmedDC);
	_dxImmedDC->Draw( 4, 0 );

	// restore Input Layout & Primitive Topology
	_dxImmedDC->IASetInputLayout(ia);
	_dxImmedDC->IASetPrimitiveTopology(primTopology);
}

void MyApp::renderScene() 
{
	static float time = 0.0f;

	if (!m_bPaused)
		time += 0.001f;

    // Clear the back buffer 
	float clearColor[3] = { 0.4f, 0.4f, 0.7f };
	float clearColorBlack[3] = { 0.f, 0.f, 0.f };
    _dxImmedDC->ClearRenderTargetView(_renderTargetView, (float*)&clearColorBlack);
    _dxImmedDC->ClearDepthStencilView(_depthStencilView, D3D11_CLEAR_DEPTH, 1, 0);

	_dxImmedDC->OMSetBlendState(0, 0, 0xffffffff);
	_dxImmedDC->RSSetState(0);

    // Bind the input layout
	_dxImmedDC->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	
	XMMATRIX mVP = m_camera.getViewMatrix()*m_camera.getProjectionMatrix();

	//_______I m p l i c i t    O b j e c t s_____________
	drawImplicitToTexture();

	setMatrixVar(m_fxQuads, (void*)&mVP, "mViewProj");
	setSrvArrayVar(m_fxQuads, &m_srvImplicit, "texarrObjects", 0, 5);

	_dxImmedDC->IASetInputLayout(0);
	_dxImmedDC->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST);
	ID3DX11EffectTechnique* tech;
	tech = m_fxQuads->GetTechniqueByIndex(0);
	tech->GetPassByIndex(0)->Apply(0, _dxImmedDC);
	_dxImmedDC->Draw(4, 0);
	
	//_______T e s s e l l a t e d   O b j e c t s _______
	
	if (glb_bWireframe)
		_dxImmedDC->RSSetState(rs_Wireframe);

	// Scene - Spheres
	for (unsigned int i=0; i<m_spheres.size(); i++) {
		drawTessSphereFromOct(&(m_spheres[i]));
	}

	
	myAlien.setPos(0,30,0);
	if (myAlien.type == AlienTypes::GS_ALIEN)
	drawMesh(myAlien.mesh, m_fxAlienGS, myAlien.getMatrix());
	else if (myAlien.type == AlienTypes::EXP_ALIEN) {
		drawMesh(myAlien.mesh, m_fxExplosion, myAlien.getMatrix(), myAlien.expl_time);
		myAlien.expl_time += 0.001f;
	}

	XMFLOAT4X4 mI;
	XMStoreFloat4x4(&mI, XMMatrixIdentity());

	// Scene - Aliens
	for (unsigned int i=0; i<m_aliens.size(); i++) 
	{
		if (m_aliens[i].type == AlienTypes::VS_ALIEN)
			drawMesh(m_aliens[i].mesh, m_fxAlienVS, m_aliens[i].getMatrix());

		else if (m_aliens[i].type == AlienTypes::GS_ALIEN)
			drawMesh(m_aliens[i].mesh, m_fxAlienGS, m_aliens[i].getMatrix());

		else if (m_aliens[i].type == AlienTypes::HS_ALIEN)
			drawTessMesh(m_aliens[i].getMatrix());

		else if (m_aliens[i].type == AlienTypes::EXP_ALIEN) {
			drawMesh(m_aliens[i].mesh, m_fxExplosion, m_aliens[i].getMatrix(), m_aliens[i].expl_time);
			m_aliens[i].expl_time += 0.001f;
		}
		else if (m_aliens[i].type == AlienTypes::BEZ_ALIEN) // BEZ_ALIEN
			drawTessBezierSurface(m_aliens[i].getPos(), m_aliens[i].getTargetPos());
		else // PSP_ALIEN
			drawPSPSurface(m_aliens[i].getPos(), m_aliens[i].getTargetPos());

	}

	// PSP Surface
	if (glb_bSphereMesh)
		drawTessSphereFromMesh();


	// Terrain
	editTerrain();
	drawTessTerrain();


	//_______ G U I ______________________________________
	TwDraw();

	// Swap Buffer
	_swapChain->Present(0, 0);
}



void MyApp::drawTessTerrain()
{
	static float  time = 0.0f;
	time += 0.001f;

	if (!dx11Support())
		return;

	// set vertex buffer
	UINT stride = sizeof(Vertex);
	UINT offset = 0;
	_dxImmedDC->IASetVertexBuffers(0, 1, &m_vbSingle, &stride, &offset);

	// set topology
	_dxImmedDC->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST);
	// set input layout
	_dxImmedDC->IASetInputLayout(_inputLayout);

	// effect params
	XMMATRIX mWVP = XMMatrixIdentity() * m_camera.getViewMatrix() * m_camera.getProjectionMatrix();
	setMatrixVar(m_fxTessTerrain, (void*)&mWVP, "m_WorldViewProj");
	setFloatVar(m_fxTessTerrain, glb_insideTess, "fInsideTess");
	setFloatVar(m_fxTessTerrain, glb_edgeTess, "fEdgeTess");
	setFloatVar(m_fxTessTerrain, glb_terrainHeight, "fHeightController");
	passLightParams(m_fxTessTerrain);

	// draw
	ID3DX11EffectTechnique* fxTech;
	if (glb_selectedTessPartitioning == TessPartitioning::INTEGER)
		fxTech = m_fxTessTerrain->GetTechniqueByName("TessBezierSurface_Integer");
	else if (glb_selectedTessPartitioning == TessPartitioning::FRAC_EVEN)
		fxTech = m_fxTessTerrain->GetTechniqueByName("TessBezierSurface_FracEven");
	else 
		fxTech = m_fxTessTerrain->GetTechniqueByName("TessBezierSurface_FracOdd");

	int patch_dim  = 5; 


	float patch_size = GRID_WIDTH / (float)glb_terrainPatchDim;
	setIntVar(m_fxTessTerrain, glb_terrainPatchDim, "iNumPatch");
	setFloatVar(m_fxTessTerrain, patch_size, "fPatchSize");
	setBoolVar(m_fxTessTerrain, true, "bViewAdaptiveTess"); // [!]
	setFloatVar(m_fxTessTerrain, GRID_WIDTH, "fWidth");
	setVectorVar(m_fxTessTerrain, (void*)&m_camera.getPosVector(), "vEyePos");
	setSrvVar(m_fxTessTerrain, m_srvTerrainHmap, "texTerrainHeightmap");
	setSrvVar(m_fxTessTerrain, m_srvTerrainNmap, "texTerrainNormalmap");
	setBoolVar(m_fxTessTerrain, glb_bOn, "bOn");
	setFloatVar(m_fxTessTerrain, time, "fTime");
	setFloatVar(m_fxTessTerrain, glb_terrainMinTessFactor, "fMinTessFactor");
	setFloatVar(m_fxTessTerrain, glb_terrainMaxTessFactor, "fMaxTessFactor");
	setFloatVar(m_fxTessTerrain, glb_terrainMinViewDist, "fMinViewDist");
	setFloatVar(m_fxTessTerrain, glb_terrainMaxViewDist, "fMaxViewDist");
	setBoolVar(m_fxTessTerrain, glb_bTerrainDebugView, "bDebug");
	
	
	fxTech->GetPassByIndex(0)->Apply(0, _dxImmedDC);


	_dxImmedDC->DrawInstanced(1, glb_terrainPatchDim*glb_terrainPatchDim, 0, 0);
}


void MyApp::drawPSPSurface(XMFLOAT3 pos, XMFLOAT3 target)
{
	
	if (!dx11Support())
		return;

	// set vertex buffer
	UINT stride = sizeof(Vertex);
	UINT offset = 0;
	_dxImmedDC->IASetVertexBuffers(0, 1, &m_vbSingle, &stride, &offset);

	// set topology
	_dxImmedDC->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST);
	// set input layout
	_dxImmedDC->IASetInputLayout(_inputLayout);


	// effect params
	XMMATRIX mWVP = XMMatrixIdentity() * m_camera.getViewMatrix() * m_camera.getProjectionMatrix();
	setMatrixVar(m_fxTessPSPSurface, (void*)&mWVP, "m_WorldViewProj");
	setFloatVar(m_fxTessPSPSurface, glb_insideTess, "fInsideTess");
	setFloatVar(m_fxTessPSPSurface, glb_edgeTess, "fEdgeTess");
	setFloatVar(m_fxTessPSPSurface, glb_PSPDelta, "fDelta");
	setFloatVar(m_fxTessPSPSurface, glb_PSPScale, "fScale");
	setVectorVar(m_fxTessPSPSurface, (void*)&pos, "vPosition");
	setVectorVar(m_fxTessPSPSurface, (void*)&target, "vTargetPos");

	// draw
	ID3DX11EffectTechnique* fxTech;
	if (glb_selectedTessPartitioning == TessPartitioning::INTEGER)
		fxTech = m_fxTessPSPSurface->GetTechniqueByName("TessPSPSurface_Integer");
	else if (glb_selectedTessPartitioning == TessPartitioning::FRAC_EVEN)
		fxTech = m_fxTessPSPSurface->GetTechniqueByName("TessPSPSurface_FracEven");
	else 
		fxTech = m_fxTessPSPSurface->GetTechniqueByName("TessPSPSurface_FracOdd");

	fxTech->GetPassByIndex(0)->Apply(0, _dxImmedDC);

	_dxImmedDC->Draw(1, 0);

}

void MyApp::drawTessMesh(const XMFLOAT4X4& mWorld)
{
	if (!dx11Support())
		return;

	// set vertex buffer
	UINT stride = sizeof(Vertex);
	UINT offset = 0;
	_dxImmedDC->IASetVertexBuffers(0, 1, &m_alienModel.vb, &stride, &offset);
	_dxImmedDC->IASetIndexBuffer(m_alienModel.ib, DXGI_FORMAT_R32_UINT, 0);

	// set topology
	_dxImmedDC->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST);
	// set input layout
	_dxImmedDC->IASetInputLayout(_inputLayout);

	XMMATRIX mVP = m_camera.getViewMatrix() * 
					m_camera.getProjectionMatrix();

	// effect params
	XMMATRIX mWVP = XMLoadFloat4x4(&mWorld) * m_camera.getViewMatrix() * m_camera.getProjectionMatrix();
	setMatrixVar(m_fxTessMesh, (void*)&mWVP, "m_WorldViewProj");
	setMatrixVar(m_fxTessMesh, (void*)&mWorld, "m_World");
	setMatrixVar(m_fxTessMesh, (void*)&mVP, "mViewProj");

	setFloatVar(m_fxTessMesh, glb_insideTess, "fInsideTess");
	setFloatVar(m_fxTessMesh, glb_edgeTess, "fEdgeTess");
	setFloatVar(m_fxTessMesh, glb_sphereRadius, "fRadius");
	setSrvVar(m_fxTessMesh, m_srvTerrainHmap, "texTerrainHeight");

	passLightParams(m_fxTessMesh);

	// draw
	ID3DX11EffectTechnique* fxTech;
	if (glb_selectedTessPartitioning == TessPartitioning::INTEGER)
		fxTech = m_fxTessMesh->GetTechniqueByName("TessSphere_Integer");
	else if (glb_selectedTessPartitioning == TessPartitioning::FRAC_EVEN)
		fxTech = m_fxTessMesh->GetTechniqueByName("TessSphere_FracEven");
	else 
		fxTech = m_fxTessMesh->GetTechniqueByName("TessSphere_FracOdd");
	
	fxTech->GetPassByIndex(0)->Apply(0, _dxImmedDC);

	_dxImmedDC->DrawIndexed(m_alienModel.numIndices, DXGI_FORMAT_R32_UINT, 0);
}



void MyApp::drawMesh(const Mesh& mesh, ID3DX11Effect* fx, const XMFLOAT4X4& mWorld, float time)
{
	// Bind vertex buffer
    UINT stride = sizeof(Vertex), offset = 0;
	_dxImmedDC->IASetVertexBuffers(0, 1, &(mesh.model->vb), &stride, &offset);

	// Bind index buffer
	_dxImmedDC->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	_dxImmedDC->IASetIndexBuffer(mesh.model->ib, DXGI_FORMAT_R32_UINT, 0);


	XMMATRIX mWVP = XMLoadFloat4x4(&mWorld) * m_camera.getViewMatrix() * 
		m_camera.getProjectionMatrix();
	
	passLightParams(fx);
	
	setFloatVar(fx, glb_fThornLength, "fThornLength");
	setMatrixVar(fx, (void*)&mWorld,							"m_World");
	setMatrixVar(fx, (void*)&mWVP,								"m_WorldViewProj");
	setMatrixVar(fx, (void*)&m_camera.getViewMatrix(),			"m_View");
	setMatrixVar(fx, (void*)&m_camera.getProjectionMatrix(),	"m_Proj");
	setSrvVar(fx, m_srvRandom,									"tex_noise");
	setSrvVar(fx,		m_srvTerrainHmap,						"texTerrainHeight");
	if (time > 0.0f)
		setFloatVar(fx, time, "fTime");


	// Material
	setMaterial(fx, mesh.material);

	ID3DX11EffectTechnique* _technique = fx->GetTechniqueByIndex(0);
	_technique->GetPassByIndex(0)->Apply(0, _dxImmedDC);

	// Draw
	_dxImmedDC->DrawIndexed(mesh.model->numIndices, 0, 0);
}

void MyApp::onKey(WPARAM key_code)
{

	XMFLOAT4 camPos = m_camera.getPosVector();
	XMMATRIX m;
	std::fstream fout; 
	XMFLOAT4 x,y,z,p;

	float camera_step = 0.05f;

	switch (key_code)
	{	
		case VK_LEFT: // left
			break;
		case VK_RIGHT:	// right
			break;
		case VK_UP:
			break;
		case VK_DOWN:
			break;

		case 112 : // F1
			
			break;

		case 113:  // F2
			
			break;

		case 114:	// F3
			fout.open("g:/mout.txt", std::ios::out);
			assert(fout);
			
			x = m_camera.getRightVector();
			y = m_camera.getUpVector();
			z = m_camera.getLookVector();
			p = m_camera.getPosVector();
			
			fout	<< x.x << " " << y.x << " " << z.x << std::endl
					<< x.y << " " << y.y << " " << z.y << std::endl
					<< x.z << " " << y.z << " " << z.z << std::endl
					<< p.x << " " << p.y << " " << p.z << std::endl;

			fout.close();

			break;

		case 115:	// F4
		
			break;

		case 116:	// F5
		
			break;

		case 0x41:	// A
			break;

		case 0x44:	// D
			break;

		case 0x53:	// S
			break;

		case 80:	// P
			m_bPaused = !m_bPaused;

		case 0x57:	// W
			break;

		case 36:	// numpad 7 
			camPos.y -= 1.1f;
			m_camera.setPosVector(camPos.x, camPos.y, camPos.z);
			break;
		case 33:	// numpad 9 - rotate globe
			camPos.y += 1.1f;
			m_camera.setPosVector(camPos.x, camPos.y, camPos.z);
			break;
		
		
	}
}


void MyApp::setupScene()
{

	int N_SPHERES = 8;
	float SKY_HEIGHT = 175.0f;

	float MIN_RAD = 5;
	float MAX_RAD = 15;

	int MIN_TESSFACTOR = 2;
	int MAX_TESSFACTOR = 7;


	float spheres_area = GRID_WIDTH;

	for (int i=0; i<N_SPHERES; i++)
	{
		Sphere s;
		s.setPos(	rand() % (unsigned int)spheres_area - spheres_area / 2.0f,
					SKY_HEIGHT,
					rand() % (unsigned int)spheres_area - spheres_area / 2.0f
				);

		s.setTargetPos(s.getPos().x, 25.0f, s.getPos().z);
		float rnd = rand() / (float)RAND_MAX;
		s.radius = MIN_RAD + rnd * (MAX_RAD - MIN_RAD);
		s.tessFactor = rand() % (MAX_TESSFACTOR - MIN_TESSFACTOR + 1) + MIN_TESSFACTOR;

		m_spheres.push_back(s);
	}

	// tess objects parameters
	for (int i=0; i<4; i++) {
		tessObjects[i].bWireframe = false;
		tessObjects[i].iEdgeFactor = 10;
		tessObjects[i].iInsideFactor = 10;
		tessObjects[i].partitioning = TessPartitioning::INTEGER;
	}
}


void MyApp::passLightParams(ID3DX11Effect* fx)
{
	setVectorVar(fx, &light.dir, "vLightDir");
	setVectorVar(fx, &light.diffuse, "vLightColor");
}